let () = Random.self_init ()

let adresse = ref None
let port = ref 45678
let nom = ref None

let echec_adresse = ref false

let toujours_prendre = ref false

let mode_encheres = ref Client_unix.Performant
let mode_ecart = ref Client_unix.Performant
let mode_jeu = ref Client_unix.Performant
let suivi_partie = ref false

let set_adresse chaine =
  try
    adresse := Some ((Unix.gethostbyname chaine).Unix.h_addr_list)
  with
  | _ -> echec_adresse := true

let set_port port_donne =
    port := port_donne

let set_nom nom_donne =
  nom := Some nom_donne

let set_mode_encheres = function
  | "suivi-points" -> mode_encheres := Client_unix.SuiviPoints
  | "suivi-reflexion" -> mode_encheres := Client_unix.SuiviReflexion
  | _ -> ()
	   
let set_mode_ecart = function
  | "suivi-points" -> mode_ecart := Client_unix.SuiviPoints
  | "suivi-reflexion" -> mode_ecart := Client_unix.SuiviReflexion
  | _ -> ()
	   
let set_mode_jeu = function
  | "suivi-points" -> mode_jeu := Client_unix.SuiviPoints
  | "suivi-reflexion" -> mode_jeu := Client_unix.SuiviReflexion
  | _ -> ()

let invitation = [ref "" ; ref "" ; ref "" ; ref ""]

let err = Unix.out_channel_of_descr Unix.stderr

let usage = "Client autonome de jeu de tarot suivant le protocole Tarot Virtuel. Arguments :"

let afficher_aide () = 
  print_endline usage ;
  print_endline "\t-a adresse, -adresse adresse, --adresse adresse, -address adresse, --address adresse : spécifie l'adresse du serveur. Il n'y a pas de valeur par défaut." ;
  print_endline "\t-p port, -port port : spécifie le port du servuce. Par défaut, c'est 45678." ;
  print_endline "\t-n nom, -nom nom : spécifie le nom à utiliser. Il n'y a pas de valeur par défaut. Il est conseillé d'encoder le nom en UTF-8. Le nom doit faire au plus 16 octets. Pas de spécification sur le nombre de caractères dans un certain encodage.";
  print_endline "\t-i nom1 nom2 nom3 nom4, -inviter nom1 nom2 nom3 nom4 : spécifie les noms des joueurs à inviter.";
  print_endline "\t-mode-encheres [suivi-points, suivi-reflexion, normal] : spécifie le mode d'exécution de l'algorithme dans la phase d'enchères. suivi-points vous permet de suivre au tour par tour les points espérés et les points accumulés, suivi-reflexion est un mode interactif qui vous permet de suivre la réflexion de l'IA, normal est le mode par défaut (pour toutes les phases) où rien n'est affiché.";
  print_endline "\t-mode-ecart [suivi-points, suivi-reflexion, normal] : spécifie le mode d'exécution dans la phase de constitution d'écart.";
  print_endline "\t-mode-jeu [suivi-points, suivi-reflexion, normal, reglage-prise] : spécifie le mode d'exécution dans la phase de constitution de jeu.";
  print_endline "\t-toujours-prendre : garde quel que soit le jeu.";
  print_endline "\t-suivi-partie : affiche les résultats de tout le monde à la fin de chaque partie.";
  print_endline "\t-h, -aide, --aide, -help, --help : Affiche ce message et quitte.";
  exit 0

let speclist =
  [("-a", Arg.String (set_adresse), "") ;
   ("-adresse", Arg.String (set_adresse), "") ;
   ("-p", Arg.Int (set_port), "") ;
   ("-port", Arg.Int (set_port), "") ;
   ("-n", Arg.String (set_nom), "") ;
   ("-nom", Arg.String (set_nom), "") ;
   ("-i", Arg.Tuple
	    (List.map
	       (fun r -> Arg.Set_string r)
	       invitation), "") ;
   ("-inviter", Arg.Tuple
		  (List.map
		     (fun r -> Arg.Set_string r)
		     invitation), "") ;
   ("-mode-encheres", Arg.Symbol
			(["normal" ; "suivi-points" ; "suivi-reflexion"], set_mode_encheres), "") ;
   ("-mode-ecart", Arg.Symbol
		     (["normal" ; "suivi-points" ; "suivi-reflexion"], set_mode_ecart), "") ;
   ("-mode-jeu", Arg.Symbol
		   (["normal" ; "suivi-points" ; "suivi-reflexion" ; "reglage-prise"], set_mode_jeu), "") ;
   ("-toujours-prendre", Arg.Set toujours_prendre, "") ;
   ("-suivi-partie", Arg.Set suivi_partie, "");
   ("-h", Arg.Unit (afficher_aide), "") ;
   ("-aide", Arg.Unit (afficher_aide), "") ;
   ("--aide", Arg.Unit (afficher_aide), "") ;
   ("-help", Arg.Unit (afficher_aide), "") ;
   ("--help", Arg.Unit (afficher_aide), "")]

let () = Arg.parse speclist (Printf.printf "Je ne sais pas quoi faire de %s.\n%!") usage

let () = 
  match (!adresse, !port, !nom, !echec_adresse) with
  | (Some [||], _, _, _) ->
     let () = Printf.eprintf "Erreur : impossible de résoudre l'adresse du serveur.\n%!" in
     ()
  | (Some addr, port, Some nom, false) 
      when port >= 0 && port <= 65535 && String.length nom <= 16 ->
     (* Tout est bon *)
     let invitation =
       List.map
	 (fun r -> !r)
	 invitation
     in
     let invitation =
       match invitation with
       | ["" ; "" ; "" ; ""] -> None
       | _ -> Some invitation
     in
     let rec ouvrir_connexion i =
       if i < Array.length addr then
	 try
	   Unix.open_connection (Unix.ADDR_INET (addr.(i), port))
	 with
	 | _ -> ouvrir_connexion (i + 1)
       else
	 let () = Printf.eprintf "Erreur : Impossible de se connecter au serveur.\n%!" in
	 failwith "connexion impossible"
     in
     let fonction = Client_unix.jouer
		      0.001
		      200
		      30. 5. 5.
		      !suivi_partie
		      !mode_encheres !mode_ecart !mode_jeu
		      !toujours_prendre 5. nom invitation in
     let jouer () =
       try
	 let (input, output) = ouvrir_connexion 0 in
	 let () =
	   try
	     fonction input output
	   with e ->
	     let () = Printf.printf "Exception !\n%s\n%!"
				    (Printexc.to_string e) in
	     let () = Printexc.print_backtrace stdout in
	     exit 2
	 in
	 let () = Printf.printf "Fini.\n%!" in
	 true
       with
       | exc ->
	  let () = Printf.printf "Exception !\n%s\n%!"
				(Printexc.to_string exc) in
	  false
     in
     while jouer () do () done
  | (None, _, _, true) -> 
     output_string err "Erreur : l'adresse spécifiée n'a pas le bon format. Essayez --help\n"
  | (Some _, _, _, true) ->
     failwith "Ne peut pas arriver : si l'adresse est invalide je ne l'ai pas validée."
  | (None, _, _, false) ->
     output_string err "Erreur : vous devez spécifier l'adresse du serveur. Essayez --help\n"
  | (Some _, _, None, false) ->
     output_string err "Erreur : vous devez spécifier votre nom. Essayez --help\n"
  | (Some _, _, Some _, false) ->
     output_string err "Erreur : votre nom doit faire au plus 16 octets et le numéro de port doit être entre 0 et 65535 inclus. Essayez --help\n"
